package com.jgoodies.looks.plastic;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableUI;
import java.awt.event.ActionEvent;

/**
 * Created by IntelliJ IDEA.
 * User: babitsky.viacheslav
 * Date: 04.03.2008
 * Time: 11:45:59
 */
public class PlasticXPTableUI extends BasicTableUI {
    private static final String START_EDITING = "startEditing";

    public static ComponentUI createUI(JComponent b) {
        return new PlasticXPTableUI();
    }

    public PlasticXPTableUI() {
    }

    /**
     * Register all keyboard actions on the JTable.
     */
    protected void installKeyboardActions() {
        super.installKeyboardActions();
        ActionMap map = (ActionMap) UIManager.get("Table.actionMap");
        if (map != null) {
            map.put("selectNextColumnCell",
                    new NavigationalAction());
            map.put(START_EDITING, new AbstractAction() {
                /**
                 * Invoked when an action occurs.
                 */
                public void actionPerformed(ActionEvent e) {
                    ListSelectionModel rsm = table.getSelectionModel();
                    int leadRow = getAdjustedLead(table, true, rsm);

                    ListSelectionModel csm = table.getColumnModel().getSelectionModel();
                    int leadColumn = getAdjustedLead(table, false, csm);
                    if (!table.hasFocus()) {
                        CellEditor cellEditor = table.getCellEditor();
                        if (cellEditor != null && !cellEditor.stopCellEditing()) {
                            return;
                        }
                        table.requestFocus();
                        return;
                    }
                    table.editCellAt(leadRow, leadColumn, e);
                    JComponent editorComp = (JComponent) table.getEditorComponent();
                    if (editorComp != null) {
                        editorComp.requestFocus();
                        editorComp.putClientProperty("selectOnFocus", Boolean.TRUE);
                    }
                }


            });
        }
        SwingUtilities.replaceUIActionMap(table, map);
    }

    private static class NavigationalAction extends AbstractAction {
        protected final int dx = 1;
        protected final int dy = 0;

        protected int anchorRow;
        protected int anchorColumn;
        protected int leadRow;
        protected int leadColumn;

        protected NavigationalAction() {
        }

//        private int clipToRange(int i, int a, int b) {
//            return Math.min(Math.max(i, a), b - 1);
//        }

        //

        private int selectionSpan(ListSelectionModel sm) {
            return sm.getMaxSelectionIndex() - sm.getMinSelectionIndex() + 1;
        }

        private int compare(int i, ListSelectionModel sm) {
            return compare(i, sm.getMinSelectionIndex(), sm.getMaxSelectionIndex() + 1);
        }

        private int compare(int i, int a, int b) {
            return (i < a) ? -1 : (i >= b) ? 1 : 0;
        }

        private boolean moveWithinSelectedRange(JTable table, int dx, int dy, boolean ignoreCarry) {
            ListSelectionModel rsm = table.getSelectionModel();
            ListSelectionModel csm = table.getColumnModel().getSelectionModel();

            int newAnchorRow = anchorRow + dy;
            int newAnchorColumn = anchorColumn + dx;

            int rowSgn;
            int colSgn;
            int rowCount = selectionSpan(rsm);
            int columnCount = selectionSpan(csm);

            boolean canStayInSelection = (rowCount * columnCount > 1);
            if (canStayInSelection) {
                rowSgn = compare(newAnchorRow, rsm);
                colSgn = compare(newAnchorColumn, csm);
            } else {
                // If there is only one selected cell, there is no point
                // in trying to stay within the selected area. Move outside
                // the selection, wrapping at the table boundaries.
                rowCount = table.getRowCount();
                columnCount = table.getColumnCount();
                rowSgn = compare(newAnchorRow, 0, rowCount);
                colSgn = compare(newAnchorColumn, 0, columnCount);

            }

            anchorRow = newAnchorRow - rowCount * rowSgn;
            anchorColumn = newAnchorColumn - columnCount * colSgn;

            if (!ignoreCarry) {
                return moveWithinSelectedRange(table, rowSgn, colSgn, true);
            }
            return canStayInSelection;
        }

        public void actionPerformed(ActionEvent e) {
            JTable table = (JTable) e.getSource();
            ListSelectionModel rsm = table.getSelectionModel();
            anchorRow = rsm.getAnchorSelectionIndex();
            leadRow = rsm.getLeadSelectionIndex();

            ListSelectionModel csm = table.getColumnModel().getSelectionModel();
            anchorColumn = csm.getAnchorSelectionIndex();
            leadColumn = csm.getLeadSelectionIndex();

            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
                return;
                /*if (!table.getCellEditor().stopCellEditing()) {
                    return;
                } */
            }

            if (moveWithinSelectedRange(table, dx, dy, false)) {
                table.changeSelection(anchorRow, anchorColumn, true, true);
            } else {
                table.changeSelection(anchorRow, anchorColumn, false, false);
            }
            //if (table.getModel() instanceof AtlasDefaultTreeTableModel && anchorColumn ==0) return;
            if (!table.getModel().isCellEditable(anchorRow, anchorColumn)) {
                for (int i = table.getModel().getColumnCount() - 1; i >= 0; i--) {
                    if (table.getModel().isCellEditable(anchorRow, i)) {
                        actionPerformed(e);
                        break;
                    }
                }
            }
        }
    }

    private static int getAdjustedLead(JTable table,
                                       boolean row,
                                       ListSelectionModel model) {

        int index = model.getLeadSelectionIndex();
        int compare = row ? table.getRowCount() : table.getColumnCount();
        return index < compare ? index : -1;
    }


}
